// License.cpp: implementation of the License class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "iphlpapi.h"
#include "LicenseManager.h"
#include "CryptUtility.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

LicenseManager::LicenseManager(const std::string& strMacAddr, const std::string& strDesc, DWORD dwExpiration, DWORD dwEvaluationPeriod):
	m_strDesc(strDesc),
	m_dwPermission(LIC_DEFAULTPERMISSION),
	m_dwExpiration(dwExpiration),
	m_dwEvaluationPeriod(dwEvaluationPeriod)
{
	if (!strMacAddr.empty()) SetMacAddrString(strMacAddr);

	ZeroMemory(m_byConcurrentNumber, sizeof(m_byConcurrentNumber));
}

LicenseManager::~LicenseManager()
{
}

void LicenseManager::SetMacAddrString(const std::string& strMacAddr)
{
	ASSERT(strMacAddr.size() == 6*2);
	ZeroMemory(m_byMacAddr, 6);
	for (int i = 0;i < strMacAddr.size();i++)
	{
		BYTE by = 0;
		char c = strMacAddr[i];
		if (c >= '0' && c <= '9')
		{
			by = c-'0';
		}
		else if (c >= 'A' && c <= 'F')
		{
			by = 10+c-'A';
		}
		else
		{
			by = 0;
			ASSERT(false);
		}
		m_byMacAddr[i/2] |= (i%2 == 0?by<<4:by);
	}
}

void LicenseManager::GetMacAddrString(std::string& strMacAddr) const
{
	strMacAddr = "";

	char szMacAddr[3];
	for (int i = 0;i < 6;i++)
	{
		sprintf(szMacAddr, "%02X", m_byMacAddr[i]);
		strMacAddr.append(szMacAddr);
	}
}

bool LicenseManager::GetPermission(int nId) const
{
	return (m_dwPermission&(1<<nId)) != 0;
}

bool LicenseManager::ChangePermission(int nId, bool bPermit)
{
	if (nId == -1)
	{
		if (bPermit)
		{
			m_dwPermission = LIC_DEFAULTPERMISSION;
		}
		else
		{
			m_dwPermission = 0;
		}
	}
	else
	{
		if (bPermit)
		{
			m_dwPermission |= 1<<nId;
		}
		else
		{
			m_dwPermission &= ~(1<<nId);
		}
	}

	return GenerateKey();
}

bool LicenseManager::GenerateKey()
{
	if (!IsExpirationValid()) return false;

	BYTE byLicense[sizeof(DWORD)*LIC_NUMOFDWORD];
	memcpy(byLicense, (BYTE *)&m_dwExpiration, sizeof(DWORD));
	//memcpy(&byLicense[sizeof(DWORD)], (BYTE *)&m_dwPermission, sizeof(DWORD));
	memcpy(&byLicense[sizeof(DWORD)], (BYTE *)&m_dwPermission, 1);
	memcpy(&byLicense[sizeof(DWORD)+1], m_byConcurrentNumber, 3);
	memcpy(&byLicense[sizeof(DWORD)*2], (BYTE *)&m_dwEvaluationPeriod, sizeof(DWORD));
	ZeroMemory(&byLicense[sizeof(DWORD)*3], sizeof(DWORD));

	return CryptUtility::Encrypt(byLicense, m_byLicenseKey, sizeof(byLicense), (BYTE *)m_byMacAddr, 6);
}

void LicenseManager::GetKeyString(std::string& strKey) const
{
	char szKey[3];

	for (int i = 0;i < LIC_KEYLEN;i++)
	{
		sprintf(szKey, "%02X", m_byLicenseKey[i]);
		strKey.append(szKey);
	}
}

DWORD LicenseManager::ApplyKey(const std::string& strKey)
{
	m_dwExpiration = 0;
	m_dwPermission = 0;
	ZeroMemory(m_byConcurrentNumber, sizeof(m_byConcurrentNumber));
	m_dwEvaluationPeriod = 0;

	const int nSize = strKey.size();
	if (nSize == LIC_KEYLEN*2)
	{
		BYTE byKey = 0;
		for (int i = 0;i < nSize;i++)
		{
			char c = strKey[i];
			BYTE by = 0;
			if (c >= '0' && c <= '9')
			{
				by = c-'0';
			}
			else if (c >= 'A' && c <= 'F')
			{
				by = 10+c-'A';
			}
			else
			{
				TRACE0("Incorrect character found!");
				break;
			}
			m_byLicenseKey[i/2] = (i%2)?(m_byLicenseKey[i/2]|by):(by<<4);
		}

		if (i == nSize)
		{
			BYTE byLicense[LIC_KEYLEN];
			if (CryptUtility::Decrypt(m_byLicenseKey, (BYTE *)byLicense, sizeof(m_byLicenseKey), (BYTE *)m_byMacAddr, 6))
			{
				m_dwExpiration = *(DWORD *)byLicense;
				//m_dwPermission = *(DWORD *)&byLicense[sizeof(DWORD)];
				m_dwPermission = byLicense[sizeof(DWORD)];
				memcpy(m_byConcurrentNumber, &byLicense[sizeof(DWORD)+1], sizeof(m_byConcurrentNumber));
				m_dwEvaluationPeriod = *(DWORD *)&byLicense[sizeof(DWORD)*2];

				if (!IsExpirationValid() || !IsEvaluationPeriodValid())
				{
					m_dwExpiration = 0;
					m_dwPermission = 0;
					ZeroMemory(m_byConcurrentNumber, sizeof(m_byConcurrentNumber));
					m_dwEvaluationPeriod = 0;
				}
			}
			else
			{
				TRACE0("Decryption failure!");
				m_dwExpiration = 0;
				m_dwPermission = 0;
				ZeroMemory(m_byConcurrentNumber, sizeof(m_byConcurrentNumber));
				m_dwEvaluationPeriod = 0;
			}
		}
	}

	return m_dwPermission;
}

void LicenseManager::GetExpirationString(std::string& strExpiration) const
{
	if (m_dwExpiration == 0)
	{
		strExpiration = "NA";
	}
	else if (m_dwExpiration == 0xffffffff)
	{
		strExpiration = "Forever";
	}
	else
	{
		char szExpiration[64];
		sprintf(szExpiration, "%02d/%02d/%04d", HIBYTE(HIWORD(m_dwExpiration)), LOBYTE(HIWORD(m_dwExpiration)), LOWORD(m_dwExpiration));
		strExpiration = szExpiration;
	}
}

bool LicenseManager::IsExpirationValid() const
{
	if (m_dwExpiration == 0xffffffff) return true;

	int nYear = LOWORD(m_dwExpiration);
	int nMonth = HIBYTE(HIWORD(m_dwExpiration));
	int nDay = LOBYTE(HIWORD(m_dwExpiration));

	return IsDateValid(nYear, nMonth, nDay);
}

bool LicenseManager::IsEvaluationPeriodValid() const
{
	int nYear = LOWORD(m_dwEvaluationPeriod);
	int nMonth = HIBYTE(HIWORD(m_dwEvaluationPeriod));
	int nDay = LOBYTE(HIWORD(m_dwEvaluationPeriod));

	return IsDateValid(nYear, nMonth, nDay);
}

bool LicenseManager::IsDateValid(int nYear, int nMonth, int nDay) const
{
	if (nYear < 2000 || nYear > 2050) return false;
	if (nMonth < 1 || nMonth > 12) return false;

	int nDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	if ((nMonth == 2) && (nYear%4 == 0) && ((nYear%400 == 0) || (nYear%100 != 0)))
	{
		nDays[1] = 29;
	}

	if (nDay < 1 || nDay > nDays[nMonth-1]) return false;

	return true;
}

bool LicenseManager::ChangeConcurrentNumber(int nId, int nNum)
{
	m_byConcurrentNumber[nId] = nNum;

	return GenerateKey();
}

int LicenseManager::GetConcurrentNumber(int nId) const
{
	return m_byConcurrentNumber[nId];
}

void LicenseManager::GetConcurrentNumberString(int nId, std::string& strConcurrentNumber) const
{
	char szConcurrentNumber[16];
	sprintf(szConcurrentNumber, "%d", m_byConcurrentNumber[nId]);
	strConcurrentNumber = szConcurrentNumber;
}

void LicenseManager::GetEvaluationPeriodString(std::string& strEvaluationPeriod) const
{
	if (m_dwEvaluationPeriod == 0)
	{
		strEvaluationPeriod = "NA";
	}
	else
	{
		char szEvaluationPeriod[64];
		sprintf(szEvaluationPeriod, "%02d/%02d/%04d", HIBYTE(HIWORD(m_dwEvaluationPeriod)), LOBYTE(HIWORD(m_dwEvaluationPeriod)), LOWORD(m_dwEvaluationPeriod));
		strEvaluationPeriod = szEvaluationPeriod;
	}
}

bool LicenseManager::GetLocalMACAddress(std::vector<std::string>& vecMACAddress, std::vector<std::string>& vecDescription)
{
	vecMACAddress.clear();

	IP_ADAPTER_INFO AdapterInfo;  // Allocate information for up to 16 NICs
	PIP_ADAPTER_INFO pAdapterInfo = &AdapterInfo; // Contains pointer to current adapter info
	DWORD dwBufLen = sizeof(AdapterInfo); // Save the memory size of buffer

	DWORD dwStatus = GetAdaptersInfo( // Call GetAdapterInfo
		&AdapterInfo, // [out] buffer to receive data
		&dwBufLen); // [in] size of receive data buffer

	if (dwStatus == ERROR_BUFFER_OVERFLOW)
	{
		pAdapterInfo = (PIP_ADAPTER_INFO)new BYTE[dwBufLen];
		dwStatus = GetAdaptersInfo(pAdapterInfo, &dwBufLen);
	}

	if (dwStatus != ERROR_SUCCESS)
	{
		return false;
	}

	PIP_ADAPTER_INFO pTempInfo = pAdapterInfo;
	do
	{
		char szMacAddr[16];
		sprintf(szMacAddr, "%02X%02X%02X%02X%02X%02X",
			pTempInfo->Address[0], pTempInfo->Address[1],
			pTempInfo->Address[2], pTempInfo->Address[3],
			pTempInfo->Address[4], pTempInfo->Address[5]);
		std::string strMaxAddr = szMacAddr;
		vecMACAddress.push_back(szMacAddr);
		std::string strDescription = pTempInfo->Description;
		vecDescription.push_back(strDescription);
		pTempInfo = pTempInfo->Next;  // Progress through linked list
	}
	while (pTempInfo);  // Terminate if last adapter

	if (pAdapterInfo != &AdapterInfo) delete pAdapterInfo;

	return true;
}